package bancosys.tec.persist.hibernate.impl;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

/**
 * A relacao de hierarquia para {@link HibernateClassDependency} so existe para manter compatibilidade.
 */
public class HibernateEntityDependency {

    // private final String dependente, dependencia, path;

    private Class<?> dependente, dependencia;

    private final String path;

    /**
     * C'tor
     * 
     * @param dependente a classe que possui o relacionamento
     * @param dependencia a classe 'alvo' - a entidade que se quer apagar
     * @param path o caminho - geralmente nome da propriedade
     */
    public HibernateEntityDependency(Class<?> dependente, Class<?> dependencia, String path) {
        // super(dependencia.getName(), dependente.getName(), path, Object.class);
        this.dependente = dependente;
        this.dependencia = dependencia;
        this.path = path;
    }

    /**
     * @return a classe que possui o relactionamento
     */
    public final String getDependente() {
        return this.dependente.getName();
    }

    /**
     * @return a classe que se quer apagar
     */
    public final String getDependencia() {
        return this.dependencia.getName();
    }

    /**
     * @return o caminho para a dependencia
     */
    public final String getPath() {
        return this.path;
    }

    /**
     * Cria uma {@link Criteria} que conta o numero de dependencias
     * 
     * @param session a {@link Session} ativa do hibernate. Deve ter uma transacao ativa
     * @param idDependencia o id da dependencia
     * @return {@link Criteria}
     */
    public Criteria createCountQuery(Session session, Serializable idDependencia) {
        return this.createBaseQuery(session, idDependencia).setProjection(Projections.rowCount());
    }

    /**
     * Conta o numero de dependencias
     * 
     * @param session a {@link Session}
     * @param pk {@link Serializable} pk
     * @return {@link Number}
     */
    public Number countDependencies(Session session, Serializable pk) {
        return (Number) this.createCountQuery(session, pk).uniqueResult();
    }

    /**
     * Cria uma {@link Criteria} que lista todas as dependencias
     * 
     * @param session a {@link Session}
     * @param idDependencia o id da dependencia
     * @return {@link Criteria}
     */
    protected Criteria createBaseQuery(Session session, Serializable idDependencia) {
        return session.createCriteria(this.dependente).createCriteria(this.path).add(
                Restrictions.idEq(idDependencia));
    }

    /**
     * @return {@link String}
     */
    @Override
    public String toString() {
        return this.dependente + "." + this.path + " -> " + this.dependencia;
    }

    /**
     * @param session a {@link Session}
     * @param idDependency o id da dependencia. Geralmente o id da entidade que se quer apagar.
     * @return {@link Criteria}
     */
    public Criteria createListQuery(Session session, Serializable idDependency) {
        return this.createBaseQuery(session, idDependency);
    }

    /**
     * Verifica se ha dependencias
     * 
     * @param session a sessao do hibernate. Deve ter uma transacao ativa.
     * @param pk o valor da chave primaria
     * @return boolean
     */
    public boolean hasDependency(Session session, Serializable pk) {
        List<?> list = this.createBaseQuery(session, pk).setProjection(Projections.id()).setMaxResults(1).list();
        return !list.isEmpty();
    }

    /**
     * @return a classe que se quer apagar
     */
    public Class<?> getTipoDependencia() {
        return this.dependencia;
    }

    /**
     * @return a classe que possui o relactionamento
     */
    public Class<?> getTipoDependente() {
        return this.dependente;
    }

}
